package com.axonactive.yolo.service.api;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;





//import com.axonactive.yolo.controller.DemoController;
import com.axonactive.yolo.dto.Airline;
import com.axonactive.yolo.dto.AirportArrival;
import com.axonactive.yolo.dto.AirportDeparture;
import com.axonactive.yolo.dto.Flight;
import com.axonactive.yolo.dto.InBound;
import com.axonactive.yolo.dto.OutBound;
import com.axonactive.yolo.dto.Price;
import com.axonactive.yolo.dto.Provider;
import com.axonactive.yolo.dtotemp.WegoFlight;
import com.axonactive.yolo.service.internal.FlightRequester;
import com.axonactive.yolo.util.IOUtil;
import com.axonactive.yolo.util.Utils;

public class TravelPayoutsAPI extends FlightRequester {
//	private final String TOKEN = "0cd9836579d33716414c7c4d086498db";
//	private final String MARKER = "29488";
	private final String URL = "http://yasen.aviasales.ru/searches_jetradar.json?locale=en";
	private final String ORIGIN_NAME = "search[params_attributes][origin_name]";
	private final String DESTINATION_NAME = "search[params_attributes][destination_name]";
	private final String DEPART_DATE = "search[params_attributes][depart_date]";
	private final String RETURN_DATE = "search[params_attributes][return_date]";
	private final String RANGE = "search[params_attributes][range]";
	private final String ADULTS = "search[params_attributes][adults]";
	private final String CHILDREN = "search[params_attributes][children]";
	private final String INFANTS = "search[params_attributes][infants]";
	private final String TRIP_CLASS = "search[params_attributes][trip_class]";

	private static final Logger LOGGER = Logger.getLogger(TravelPayoutsAPI.class);



	// Thanhvnq: insert into database json data
	Connection connect = null;
	Statement statement = null;
	PreparedStatement preparedStatement = null;
	ResultSet resultSet = null;
	private Map<Integer,APIKEY> keys;

	public TravelPayoutsAPI()
	{
		keys = new Hashtable<Integer, APIKEY>();
		keys.put(0, new APIKEY("0cd9836579d33716414c7c4d086498db", "29488"));
		keys.put(1, new APIKEY("b2884cb3294685eaff03fb75aee73a6e", "29530"));
	}

	Comparator<NameValuePair> comp = new Comparator<NameValuePair>() {
		public int compare(NameValuePair p1, NameValuePair p2) {
			return p1.getName().compareTo(p2.getName());
		}
	};

	/**
	 * MD5 hashing exactly the same result in PHP
	 *
	 * @param input
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public String md5(String input) throws NoSuchAlgorithmException {
		String result = input;
		if (input != null) {
			MessageDigest md = MessageDigest.getInstance("MD5"); // or "SHA-1"
			md.update(input.getBytes());
			BigInteger hash = new BigInteger(1, md.digest());
			result = hash.toString(16);
			while (result.length() < 32) // 40 for SHA-1
			{
				result = "0" + result;
			}
		}
		return result;
	}

	/**
	 * Implode
	 *
	 * @param separator
	 * @param data
	 * @return
	 */
	public String implode(String separator, String... data) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < data.length - 1; i++) {
			// data.length - 1 => to not add separator at the end
			if (!data[i].matches(" *")) // empty string are ""; " "; "  ";
										// and so on
			{

				sb.append(data[i]);
				sb.append(separator);
			}
		}
		sb.append(data[data.length - 1]);
		return sb.toString();
	}

	@Override
	public List<Flight> processResponse(String response) {
		try {
			JSONObject object = new JSONObject(response);

			if (object.has("tickets") != true) {
				return null;
			}
			// ticket include departure_flight and return_flight
			JSONArray listOfTickets = object.getJSONArray("tickets"); // list of
																		// Flight
																		// domain
																		// object
			JSONObject airports = object.getJSONObject("airports");
			JSONObject airlines = object.getJSONObject("airlines");
			JSONArray listOfProviders = object.getJSONArray("gates_info");

			// create list of Flight domain object
			List<Flight> flights = new ArrayList<Flight>(listOfTickets.length());

			for (int i = 0; i < listOfTickets.length(); i++) {
				Flight flight = new Flight();
				Price price = new Price();

				/**
				 * start get and set price information
				 */
				price.setAmount(listOfTickets.getJSONObject(i).getInt("total")
						+ "");
				price.setCurrencyCode("rub");
				flight.setPrice(price);
				/**
				 * stop get and set price information
				 */
				/**
				 * start get and set outbound information
				 */
				OutBound outBound = new OutBound();
				JSONArray listOfDirectFlights = listOfTickets.getJSONObject(i)
						.getJSONArray("direct_flights");
				int numberOfDepartAirportsOutBound = listOfDirectFlights
						.length();
				List<AirportDeparture> departAirportsOutBound = new ArrayList<AirportDeparture>(
						numberOfDepartAirportsOutBound);
				List<AirportArrival> arriveAirportsOutBound = new ArrayList<AirportArrival>(
						numberOfDepartAirportsOutBound);

				// number of outbound stop
				outBound.setNumberStops(String
						.valueOf(numberOfDepartAirportsOutBound - 1));
				int durationTemp = 0;
				for (int j = 0; j < numberOfDepartAirportsOutBound; j++) {
					AirportDeparture airportDeparture = new AirportDeparture();
					airportDeparture.setCode(listOfDirectFlights.getJSONObject(
							j).getString("origin"));
					airportDeparture.setName(airports.getJSONObject(
							airportDeparture.getCode()).getString("name"));
					// convert UnixTime
					String departureTime = String.valueOf(listOfDirectFlights
							.getJSONObject(j).getLong("departure"));
					airportDeparture.setDepartureTime(departureTime);
					//

					Airline airline = new Airline();
					String airlineCode = listOfDirectFlights.getJSONObject(j)
							.getString("airline");
					airline.setName(airlines.getJSONObject(airlineCode)
							.getString("name"));
					airline.setFlightClass(this.cabinClass);
					airline.setFlightNumber(String.valueOf(airlineCode
							+ listOfDirectFlights.getJSONObject(j).getInt(
									"number")));

					airportDeparture.setAirline(airline);

					// add to departAirports
					departAirportsOutBound.add(airportDeparture);
					AirportArrival airportArrival = new AirportArrival();
					airportArrival.setCode(listOfDirectFlights.getJSONObject(j)
							.getString("destination"));
					airportArrival.setName(airports.getJSONObject(
							airportArrival.getCode()).getString("name"));
					// convert UnixTime
					String arrivalTime = String.valueOf(listOfDirectFlights
							.getJSONObject(j).getLong("arrival"));
					airportArrival.setArrivalTime(arrivalTime);
					//
					airportArrival.setAirline(airline);
					// add to arriveAirports
					arriveAirportsOutBound.add(airportArrival);

					// plus duration
					durationTemp += listOfDirectFlights.getJSONObject(j)
							.getInt("duration")
							+ listOfDirectFlights.getJSONObject(j).getInt(
									"delay");

				}

				outBound.setDepartAirports(departAirportsOutBound);
				outBound.setArriveAirports(arriveAirportsOutBound);
				// set AirportExpanded
				String strAirportExpandedOB = "";
				for (int loop = 0; loop < departAirportsOutBound.size(); loop++) {
					strAirportExpandedOB += "-"
							+ departAirportsOutBound.get(loop).getCode();
				}
				strAirportExpandedOB += "-"
						+ arriveAirportsOutBound.get(
								arriveAirportsOutBound.size() - 1).getCode();
				outBound.setAirportsExpanded(strAirportExpandedOB.substring(1));
				//
				outBound.setTotalDuration(String.valueOf(durationTemp));
				outBound.setDayOver();
				flight.setOutBound(outBound);
				/**
				 * stop get and set outbound information
				 */

				if (!this.inboundDate.equals("")) {
					/**
					 * start get and set inbound information
					 */
					InBound inBound = new InBound();
					JSONArray listOfReturnFlights = listOfTickets
							.getJSONObject(i).getJSONArray("return_flights");
					int numberOfDepartAirportsInBound = listOfReturnFlights
							.length();
					List<AirportDeparture> departAirportsInBound = new ArrayList<AirportDeparture>(
							numberOfDepartAirportsInBound);
					List<AirportArrival> arriveAirportsInBound = new ArrayList<AirportArrival>(
							numberOfDepartAirportsInBound);

					// number of inbound stop
					inBound.setNumberStops(String
							.valueOf(numberOfDepartAirportsInBound - 1));
					durationTemp = 0;
					for (int j = 0; j < numberOfDepartAirportsInBound; j++) {
						AirportDeparture airportDeparture = new AirportDeparture();
						airportDeparture.setCode(listOfReturnFlights
								.getJSONObject(j).getString("origin"));
						airportDeparture.setName(airports.getJSONObject(
								airportDeparture.getCode()).getString("name"));
						// convert UnixTime
						String departureTime = String
								.valueOf(listOfReturnFlights.getJSONObject(j)
										.getLong("departure"));
						airportDeparture.setDepartureTime(departureTime);
						//
						Airline airline = new Airline();
						String airlineCode = listOfReturnFlights.getJSONObject(
								j).getString("airline");
						airline.setName(airlines.getJSONObject(airlineCode)
								.getString("name"));
						airline.setFlightClass(this.cabinClass);
						airline.setFlightNumber(String.valueOf(airlineCode
								+ listOfReturnFlights.getJSONObject(j).getInt(
										"number")));

						airportDeparture.setAirline(airline);

						// add to departAirports
						departAirportsInBound.add(airportDeparture);
						AirportArrival airportArrival = new AirportArrival();
						airportArrival.setCode(listOfReturnFlights
								.getJSONObject(j).getString("destination"));
						airportArrival.setName(airports.getJSONObject(
								airportArrival.getCode()).getString("name"));
						String arrivalTime = String.valueOf(listOfReturnFlights
								.getJSONObject(j).getLong("arrival"));
						airportArrival.setArrivalTime(arrivalTime);
						airportArrival.setAirline(airline);

						// add to arriveAirports
						arriveAirportsInBound.add(airportArrival);

						// plus duration
						durationTemp += listOfReturnFlights.getJSONObject(j)
								.getInt("duration")
								+ listOfReturnFlights.getJSONObject(j).getInt(
										"delay");
					}

					inBound.setDepartAirports(departAirportsInBound);
					inBound.setArriveAirports(arriveAirportsInBound);
					// set AirportExpanded
					String strAirportExpandedIB = "";
					for (int loop = 0; loop < departAirportsInBound.size(); loop++) {
						strAirportExpandedIB += "-"
								+ departAirportsInBound.get(loop).getCode();
					}
					strAirportExpandedIB += "-"
							+ arriveAirportsInBound.get(
									arriveAirportsInBound.size() - 1).getCode();
					inBound.setAirportsExpanded(strAirportExpandedIB
							.substring(1));
					//
					inBound.setTotalDuration(String.valueOf(durationTemp));
					inBound.setDayOver();
					flight.setInBound(inBound);
					/**
					 * stop get and set inbound information
					 */
				}

				//add providers
				JSONObject nativePrice = listOfTickets.getJSONObject(i).getJSONObject("native_prices");
				String[] name=JSONObject.getNames(nativePrice);

				List<Provider> listProviders=new ArrayList<Provider>();
				for(int j=0;j<name.length;j++){
					Provider provider=new Provider();
					provider.setPrice(nativePrice.getDouble(name[j]));
					for(int k=0;k<listOfProviders.length();k++){
						JSONObject prov=listOfProviders.getJSONObject(k);
						if(name[j].equals(prov.getInt("id")+"")){
							provider.setLabel(prov.getString("label"));
							provider.setId(prov.getInt("id"));
							provider.setCurrencyCode(prov.getString("currency_code"));
							listProviders.add(provider);
							break;
						}
					}
				}
				flight.setProviders(listProviders);

				flights.add(flight);

			}

			return flights;
		} catch (JSONException je) {
			LOGGER.info("Travel Payout : process respone exception: "+je.getMessage());
			LOGGER.error("Error",je);

		}
		return null;
	}

	@Override
	/**
	 * just temporary
	 */
	public List<WegoFlight> processResponseWego(String response) {
		return null;
	}

	@Override
	public String sendRequest() {
		// add search parameters
		List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
		urlParameters.add(new BasicNameValuePair(ORIGIN_NAME,
				this.departureAirportCode));
		urlParameters.add(new BasicNameValuePair(DESTINATION_NAME,
				this.arrivalAirportCode));
		urlParameters
				.add(new BasicNameValuePair(DEPART_DATE, this.outboundDate));
		urlParameters.add(new BasicNameValuePair(RANGE, this.rangeValue));
		urlParameters.add(new BasicNameValuePair(ADULTS, this.numAdults));
		urlParameters.add(new BasicNameValuePair(CHILDREN, this.numChildren));
		urlParameters.add(new BasicNameValuePair(INFANTS, this.numInfants));

		// cabinClass: 0 - Economy, 1 -Else
		urlParameters.add(new BasicNameValuePair(TRIP_CLASS, this.cabinClass));

		// oneway or return
		if (!this.inboundDate.equals("")) {
			urlParameters.add(new BasicNameValuePair(RETURN_DATE,
					this.inboundDate));
		}

		for (int k = 0; k < keys.size(); k++) {
			try {
				Logger.getLogger(TravelPayoutsAPI.class).info("We're using key "+k +" Thread "+Thread.currentThread().getId());
				// sort query parameters by key
				Collections.sort(urlParameters, comp);

				List<String> implodeString = new ArrayList<String>();
				implodeString.add(keys.get(k).getAPI_KEY());
				implodeString.add(keys.get(k).getTs_code());
				for (int i = 0; i < urlParameters.size(); i++) {
					implodeString.add(urlParameters.get(i).getValue());
					// System.out.println(urlParameters.get(i).getValue());
				}

				// create md5 signature from imploded string structure
				// token:maker:value[i]
				String signature = md5(implode(":",
						implodeString.toArray(new String[implodeString.size()])));

				// add token, maker and signature
				urlParameters.add(new BasicNameValuePair("signature", signature));
				urlParameters
						.add(new BasicNameValuePair("enable_api_auth", "true"));
				urlParameters.add(new BasicNameValuePair("marker", keys.get(k).getTs_code()));

				// build search url with parameters
				String searchParametes = "";
				for (int i = 0; i < urlParameters.size(); i++) {
					searchParametes += "&" + urlParameters.get(i).getName() + "="
							+ urlParameters.get(i).getValue();

				}

				URL requestUrl = new URL(URL + searchParametes);
				HttpURLConnection con = (HttpURLConnection) requestUrl
						.openConnection();
				con.setRequestMethod("POST");
				LOGGER.info("Request send to Travelpayout: " + URL + searchParametes);

				// get json response
				BufferedReader rd = new BufferedReader(new InputStreamReader(
						con.getInputStream()));
				StringBuilder sb = new StringBuilder();

				int cp;
				try {
					while ((cp = rd.read()) != -1) {
						sb.append((char) cp);
					}
				} catch (IOException e) {
					LOGGER.error("Error",e);
				}

				String json = sb.toString();
				if (!json.equals(""))
					return json;
				
				LOGGER.info("Response from Travelpayout: " + json);
		}catch (IOException io) {
			LOGGER.error("Error",io);
			LOGGER.info(new java.util.Date()+ "Travel Payout API- IOException: "+io.getMessage());
		} catch (NoSuchAlgorithmException ns) {
			LOGGER.error("Error",ns);
		}

		}//end for loop
		return "";
	}

	/* thanhvnq insert into database */
	public void insertIntoDatabase(String departureAirportCode,
			String arrivalAirportCode, String tripType, String cabinClass,
			String inboundDate, String outboundDate, String numAdults,
			String numChildren, String numInfants, String rangeValue,
			String json){

		try {
			Class.forName("com.mysql.jdbc.Driver");
			connect = DriverManager
					.getConnection("jdbc:mysql://192.168.70.42:3306/yoloflight?"
							+ "user=deployuser&password=deployuser");
			preparedStatement = connect
					.prepareStatement("insert into yoloflight.tbl_mockdb values (default,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
			preparedStatement.setString(1, departureAirportCode);
			preparedStatement.setString(2, arrivalAirportCode);
			preparedStatement.setString(3, tripType);
			preparedStatement.setString(4, cabinClass);
			preparedStatement.setString(5, inboundDate);
			preparedStatement.setString(6, outboundDate);
			preparedStatement.setString(7, numAdults);
			preparedStatement.setString(8, numChildren);
			preparedStatement.setString(9, numInfants);
			preparedStatement.setString(10, rangeValue);
			preparedStatement.setString(11, json);
			preparedStatement.executeUpdate();

		} catch (SQLException e) {
			LOGGER.error("SQLException",e);
		} catch (ClassNotFoundException e) {
			LOGGER.error("ClassNotFoundException",e);
		} finally {
			close();
		}
	}

	private void close() {
		try {
			if (resultSet != null) {
				resultSet.close();
			}

			if (statement != null) {
				statement.close();
			}

			if (connect != null) {
				connect.close();
			}
		} catch (SQLException e) {
			LOGGER.error("mysql close error", e);
		}
	}

	public static void main(String[] args) throws Exception {
		String json = "";
		String departureAirportCode = "HAN";
		String arrivalAirportCode = "LCY";
		String outboundDate = "2014-09-03";
		String inboundDate = "2014-09-08";
		String numAdults = "1";
		String numChildren = "0";
		String numInfants = "0";
		String rangeValue = "0";
		String currencyCode = "USD";
		TravelPayoutsAPI travelPayoutsAPI = new TravelPayoutsAPI();
			
		travelPayoutsAPI.setRequest(departureAirportCode,
				arrivalAirportCode, "", "", inboundDate,
				outboundDate, numAdults, numChildren, numInfants,
				rangeValue, currencyCode);
		json = travelPayoutsAPI.sendRequest();
		System.out.println(json);
		
	}

}
